﻿using HtmlAgilityPack;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Threading.Tasks;

namespace HobSoft.SoccerBet2014.Domain.Common.Site
{
    using Component;
    using Domain.Models;
    using Domain.Models.Site;
    using Domain.DataService;
    using SiteContext;
    using SoccerBet2014.Common.Json;
    using SoccerBet2014.Common.Collection;

    public partial class SboSessionManager : SessionManagerBase
    {
        public SboSessionManager(ICookiesManager cookieManager, IBetGameDataService repositoryService)
            : base(cookieManager, repositoryService)
        {

        }

        public override void Initialize(string globalHost, byte bookerId, string account)
        {
            base.Initialize(globalHost, bookerId, account);

            OddsDisplayParamters = new SboOddsDisplayParamters();
            EarlyDisplayParamters = new SboOddsDisplayParamters();
            SessionLoginName = Common.SiteContext.ContextManager.Register(bookerId, account, "global", "login_name");
        }

        public SboSessionManager(ICookiesManager cookieManager, IBetGameDataService repositoryService, string globalHost, byte bookerId, string account)
            : base(cookieManager, repositoryService, globalHost, bookerId, account)
        {
        }

        public override async Task<bool> Start()
        {
            if (!ValidInitialize())
                return false;

            Uri url = new Uri(string.Format("https://www.{0}/zh-cn/euro", GlobalHost));
            Cookie chCookie = new Cookie("lang", "zh-cn");
            CookieContainer cookies = new CookieContainer();
            cookies.Add(url, chCookie);
            var rqTask = GetHtmlRequest(url, cookies, null); ;
            HtmlHomePage = await rqTask;
            if (rqTask.IsCanceled || rqTask.Exception != null)
                return false;
            var matSite = regSiteToken.Match(HtmlHomePage);
            var matOd = regOdToken.Match(HtmlHomePage);
            if (!matSite.Success || !matOd.Success)
            {
                LoginState = LoginState.InitPageError;
                return false;
            }

            var tkeys = matSite.Groups["k"].Value.Split(',');
            var tvals = matSite.Groups["v"].Value.Split(',');
            SiteTokenKeys = tkeys.Select(kn => kn.Trim(' ', '\'')).ToList();
            SiteTokenValues = tvals.Select(kn => kn.Trim(' ', '\'')).ToList();

            var odkeys = matOd.Groups["k"].Value.Split(',');
            var odvals = matOd.Groups["v"].Value.Split(',');
            OdTokenKeys = odkeys.Select(kn => kn.Trim(' ', '\'')).ToList();
            OdTokenValues = odkeys.Select(kn => kn.Trim(' ', '\'')).ToList();
            return true;
        }

        protected virtual async Task<bool> SignInRedirectRequest(string pageHtml, string signInUrl)
        {
            HtmlDocument htmlDoc = new HtmlDocument();
            htmlDoc.LoadHtml(pageHtml);
            var form = htmlDoc.DocumentNode.SelectSingleNode("//form[@name='f']");
            string welcomeUrl = form.GetAttributeValue("action", "");
            if (string.IsNullOrEmpty(welcomeUrl))
            {
                LoginState = LoginState.LoginError;
                return false;
            }

            HtmlFormHelper inputFormHelper = new HtmlFormHelper(form.SelectNodes("//input"));
            SignupTickId = inputFormHelper.GetValueByName("id");
            SignupTickKey = inputFormHelper.GetValueByName("key");
            Uri welcomeUri = new Uri(welcomeUrl);
            Host = welcomeUri.Host;

            string htmlWelcome = string.Empty;
            string defaultPageUrl = string.Empty;
            string welcomeRedirectUrl = string.Empty;
            try
            {
                var cookies = CookiesManager.GetCookies(BookerID, Account, welcomeUri);
                var request = CreateRequest(welcomeUri, cookies, HTML_MIME, signInUrl, inputFormHelper.ToString(), "POST");
                request.AllowAutoRedirect = false;
                request.ContentType = "application/x-www-form-urlencoded";
                var resTask = request.GetResponseAsync();
                HttpWebResponse response = (HttpWebResponse)await resTask;
                CookiesManager.SetCookies(BookerID, Account, response.Cookies);
                htmlWelcome = ReadResponseStream(response);
                if (response.StatusCode == HttpStatusCode.Redirect)
                {
                    string reurl = response.Headers.Get("Location");
                    if (reurl.Contains("process-welcome.aspx"))
                        welcomeRedirectUrl = string.Format("http://{0}{1}", Host, reurl);
                    else if (reurl.Contains("default.aspx"))
                        defaultPageUrl = string.Format("http://{0}{1}", Host, reurl);
                }
                response.Close();
            }
            catch (Exception ex)
            {
                LoginState = LoginState.LoginError;
                throw ex;
            }

            if (!string.IsNullOrEmpty(welcomeRedirectUrl))
            {
                try
                {
                    Uri uri = new Uri(welcomeRedirectUrl);
                    var cookies = CookiesManager.GetCookies(BookerID, Account, uri);
                    var request = CreateRequest(uri, cookies, HTML_MIME, welcomeRedirectUrl, null, "GET");
                    request.AllowAutoRedirect = false;
                    var resTask = request.GetResponseAsync();
                    HttpWebResponse response = (HttpWebResponse)await resTask;
                    CookiesManager.SetCookies(BookerID, Account, response.Cookies);
                    htmlWelcome = ReadResponseStream(response);
                    if (response.StatusCode == HttpStatusCode.Redirect)
                    {
                        string reurl = response.Headers.Get("Location");
                        if (reurl.Contains("default.aspx"))
                            defaultPageUrl = string.Format("http://{0}{1}", Host, reurl);
                    }
                    response.Close();
                }
                catch (Exception ex)
                {
                    LoginState = LoginState.LoginError;
                    throw ex;
                }
            }

            if (string.IsNullOrEmpty(defaultPageUrl))
            {
                LoginState = LoginState.LoginError;
                return false;
            }
            var matLoginName = regLoginName.Match(defaultPageUrl);
            if (matLoginName.Success) SessionLoginName.Value = matLoginName.Groups["v"].Value;
            await RedirectToMainRequest(defaultPageUrl, welcomeRedirectUrl);
            return true;
        }

        protected virtual async Task<string> RedirectToMainRequest(string defaultPageUrl, string referer)
        {
            string htmlDefaultPage = string.Empty;
            if (!string.IsNullOrEmpty(defaultPageUrl))
            {
                if (string.IsNullOrEmpty(referer))
                    htmlDefaultPage = await GetHtmlRequest(new Uri(defaultPageUrl));
                else
                    htmlDefaultPage = await GetHtmlRequest(new Uri(defaultPageUrl), referer);

                var mat = regOddsDisplayParam.Match(htmlDefaultPage);
                if (mat.Success)
                {
                    var disparams = mat.Groups["v"].Value.Trim('[', ']').Split(',');
                    if (disparams.Length > 8)
                    {
                        int token, daylight;
                        int.TryParse(disparams[0].Trim(), out token);
                        int.TryParse(disparams[8].Trim(), out daylight);
                        OddsDisplayParamters.Token = token;
                        OddsDisplayParamters.DaylightType = daylight;
                    }
                }
                else
                {
                    OddsDisplayParamters.Token = 1;
                }
                EarlyMarketSync = new SboSyncData();
                NoLiveSync = new SboSyncData();
                LiveSync = new SboSyncData();
            }
            return htmlDefaultPage;
        }

        protected virtual async Task<bool> ConfirmTermAndConditions()
        {
            string url = string.Format("http://{0}/webroot/restricted/misc/TermAndConditions.aspx", Host);
            Uri uri = new Uri(url);
            string htmlResult = string.Empty;
            try
            {
                var cookies = CookiesManager.GetCookies(BookerID, Account, uri);
                var request = CreateRequest(uri, cookies, HTML_MIME, url, "action=%E6%88%91%E5%90%8C%E6%84%8F", "POST");
                request.ContentType = "application/x-www-form-urlencoded";
                var resTask = request.GetResponseAsync();
                HttpWebResponse response = (HttpWebResponse)await resTask;
                CookiesManager.SetCookies(BookerID, Account, response.Cookies);
                htmlResult = ReadResponseStream(response);
                response.Close();
            }
            catch (Exception ex)
            {
                LoginState = LoginState.LoginError;
                throw ex;
            }
            LoginState = LoginState.Logined;
            return true;
        }

        public override async Task<bool> Signup(string password, string token)
        {
            if (!ValidInitialize())
                return false;

            string signInUrl = string.Format("https://www.{0}/web/public/process-sign-in.aspx", GlobalHost);
            string referer = string.Format("https://www.{0}/zh-cn/euro/", GlobalHost);
            string signupFormContent = CreateSignFormContent(Account, password, token);

            CookiesManager.DeleteCookies(BookerID, Account, SessionID, new string[] { string.Format("www.{0}", GlobalHost) });

            var reqSigninTask = PostHtmlRequest(new Uri(signInUrl), referer, signupFormContent, false);
            string htmlSignupResult = await reqSigninTask;

            if (!CheckLoginHtmlState(htmlSignupResult))
                return false;

            if (htmlSignupResult.Contains("法規和條例"))
            {
                await ConfirmTermAndConditions();
                return true;
            }

            if (!htmlSignupResult.Contains("welcome.aspx"))
            {
                LoginState = LoginState.LoginError;
                return false;
            }

            var reqRedirectTask = SignInRedirectRequest(htmlSignupResult, signInUrl);
            var result = await reqRedirectTask;
            var sessionCookie = CookiesManager.GetCookie(BookerID, Account, LoginInfo.SessionID, new Uri(string.Format("http://{0}", Host)));
            var sessionValue = sessionCookie != null ? sessionCookie.Value : string.Empty;
            _loginInfo.ResetLogin(sessionValue);
            if (result) LoginState = LoginState.Logined;
            return result;
        }

        public override Task RequestSignupToken()
        {
            throw new NotImplementedException();
        }

        public override Task ChangeLauguage()
        {
            throw new NotImplementedException();
        }

        public async override Task<string> RequestMainPage()
        {
            if (!ValidSignup()) return string.Empty;
            string mainUrl = string.Format("http://{0}/web-root/restricted/default.aspx?loginname={1}", Host, SessionLoginName);
            var html = await RedirectToMainRequest(mainUrl, "");
            _loginInfo.Reset(html);

            return html;
        }

        #region request betting
        public override async Task<IList<BettingOrder>> RequestBettingHisOrder()
        {
            if (!ValidSignup()) return null;
            Uri url = FormatUri(@"http://{0}/web-root/restricted/betlist/running-bet-list.aspx", Host);
            string referer = string.Format("http://{0}/web-root/restricted/default.aspx?loginname={1}", Host, SessionLoginName);
            var reqTask = GetHtmlRequest(url, referer);
            var html = await reqTask;
            if (reqTask.IsCanceled || reqTask.Exception != null)
                return null;
            _loginInfo.Reset(html);
            return ResolveHtmlHisOrders(html);
        }

        protected virtual IList<BettingOrder> ResolveHtmlHisOrders(string html)
        {
            var betOrders = new List<BettingOrder>();
            HtmlDocument doc = new HtmlDocument();
            doc.LoadHtml(html);
            var betInfoNodes = doc.DocumentNode.SelectNodes("//table[contains(@class,'ContentTable')]//tr[@onmouseout and position()>1]");
            if (betInfoNodes == null)
                return null;
            foreach (var betInfoNode in betInfoNodes)
            {
                var oddsIdNode = betInfoNode.SelectSingleNode("td[2]/span[1]");
                var teamNode = betInfoNode.SelectSingleNode("td[3]/span[4]");
                var leagueNode = betInfoNode.SelectSingleNode("td[3]/span[6]/text()[1]");
                var dayNode = betInfoNode.SelectSingleNode("td[2]/span[3]/text()[1]");
                var timeNode = betInfoNode.SelectSingleNode("td[2]/span[3]/span/text()");
                var dayTimeNode = betInfoNode.SelectSingleNode("td[2]/span[3]");
                var statusNode = betInfoNode.SelectSingleNode("td[6]/text()");
                var moneyNode = betInfoNode.SelectSingleNode("td[5]/span[1]");
                var oddsValueNode = betInfoNode.SelectSingleNode("td[4]/span[1]");
                var typeNode = betInfoNode.SelectSingleNode("td[3]/span[3]");
                var handicapNode = betInfoNode.SelectSingleNode("td[3]/span[1]");
                if (oddsIdNode == null) continue;
                string strId = oddsIdNode != null ? oddsIdNode.InnerText.Trim() : "";
                string strState = statusNode != null ? statusNode.InnerText.Trim() : "";
                string strTeams = teamNode != null ? teamNode.InnerText.Trim() : "";
                string strLeague = leagueNode != null ? leagueNode.InnerText.Trim() : "";
                string strType = typeNode != null ? typeNode.InnerText.Trim() : "";
                string strHandicap = handicapNode != null ? handicapNode.InnerText.Trim() : "";
                string hTeam = string.Empty, vTeam = string.Empty;
                var teamArrayString = strTeams.Split(new string[] { "-对-" }, StringSplitOptions.RemoveEmptyEntries);
                if (teamArrayString.Length >= 2)
                {
                    hTeam = teamArrayString[0].Trim('-').Trim();
                    vTeam = teamArrayString[1].Trim('-').Trim();
                }
                string strBetTo = string.Empty;
                decimal handicap = 0m;
                var match = regHisOrderHandicap.Match(strHandicap);
                if (match.Success)
                {
                    if (match.Groups["t"].Success)
                    {
                        strBetTo = match.Groups["t"].Value;
                    }
                    if (match.Groups["h"].Success)
                    {
                        decimal.TryParse(match.Groups["h"].Value, out handicap);
                    }
                }
                DateTime? betTime = null;
                if (timeNode != null && dayNode != null)
                {
                    DateTime time;
                    if (DateTime.TryParse(string.Format("{0} {1}", dayNode.InnerText, timeNode.InnerText), out time))
                    {
                        var timeZone = TimeZoneInfo.FindSystemTimeZoneById("Pacific SA Standard Time");
                        betTime = TimeZoneInfo.ConvertTime(time, timeZone, TimeZoneInfo.Local);
                    }
                }
                else if (dayTimeNode != null)
                {
                    DateTime time;
                    if (DateTime.TryParse(dayTimeNode.InnerText, out time))
                    {
                        var timeZone = TimeZoneInfo.FindSystemTimeZoneById("Pacific SA Standard Time");
                        betTime = TimeZoneInfo.ConvertTime(time, timeZone, TimeZoneInfo.Local);
                    }
                }
                decimal oddsValue = 0;
                decimal money = 0m;
                decimal.TryParse(oddsValueNode != null ? oddsValueNode.InnerText.Trim() : "0", out oddsValue);
                decimal.TryParse(moneyNode != null ? moneyNode.InnerText.Trim() : "0", out money);

                var order = new BettingOrder();
                order.BmID = BookerID;
                order.Account = Account;
                order.RecID = strId;
                order.RawHomeTeam = hTeam;
                order.RawAwayTeam = vTeam;
                order.RawLeagueName = strLeague;
                order.RawTypeName = strType;
                order.Handicap = handicap;
                order.OddsValue = oddsValue;
                order.Money = money;
                order.BettingTime = betTime;
                order.RawStatus = strState;
                if (strType.IndexOf("1X2") > -1)
                    order.Type = MarketType.M1X2;
                else if (strType.IndexOf("大/小") > -1)
                    order.Type = MarketType.MOU;
                else if (strType.IndexOf("亚洲盘") > -1)
                    order.Type = MarketType.MHDP;
                switch (order.Type)
                {
                    case MarketType.M1X2:
                        if (strBetTo.IndexOf("1") > -1)
                            order.PriceType = OddsPrice.OddsPriceType.Home;
                        else if (strBetTo.IndexOf("2") > -1)
                            order.PriceType = OddsPrice.OddsPriceType.Away;
                        else if (strBetTo.IndexOf("X", StringComparison.OrdinalIgnoreCase) > -1)
                            order.PriceType = OddsPrice.OddsPriceType.Tie;
                        break;
                    case MarketType.MOU:
                        if (strBetTo.IndexOf("大") > -1)
                            order.PriceType = OddsPrice.OddsPriceType.Over;
                        else if (strBetTo.IndexOf("小") > -1)
                            order.PriceType = OddsPrice.OddsPriceType.Under;
                        break;
                    case MarketType.MHDP:
                        if (strBetTo == order.RawHomeTeam)
                            order.PriceType = OddsPrice.OddsPriceType.Home;
                        else if (strBetTo == order.RawAwayTeam)
                            order.PriceType = OddsPrice.OddsPriceType.Away;
                        break;
                    default:
                        break;
                }

                betOrders.Add(order);
            }
            return betOrders;
        }

        public async override Task<IList<BettingOrder>> RequestBettingMiniOrder()
        {
            if (!ValidSignup()) return null;
            Uri url = FormatUri(@"http://{0}//web-root/restricted/betlist/bet-list-mini-data.aspx", Host);
            string referer = string.Format("http://{0}/web-root/restricted/default.aspx?loginname={1}", Host, SessionLoginName);
            var reqTask = GetHtmlRequest(url, referer);
            var html = await reqTask;
            if (reqTask.IsCanceled || reqTask.Exception != null)
                return null;
            _loginInfo.Reset(html);
            if (string.IsNullOrEmpty(html))
                return null;

            var match = regBettingMiniOrder.Match(html);
            if (!match.Success)
                return null;
            var jsonString = match.Groups["j"].Success ? match.Groups["j"].Value : "";
            jsonString = regJsUnicode.Replace(jsonString, mat => ((char)Int32.Parse(mat.Value.Substring(2), System.Globalization.NumberStyles.HexNumber))
                .ToString(System.Globalization.CultureInfo.InvariantCulture));
            jsonString = regJsHex.Replace(jsonString, mat => ((char)Int32.Parse(mat.Value.Substring(2), System.Globalization.NumberStyles.HexNumber))
                    .ToString(System.Globalization.CultureInfo.InvariantCulture));
            var jarray = (JArray)JsonConvert.DeserializeObject(jsonString);
            List<BettingOrder> result = new List<BettingOrder>();
            if (jarray.Count == 2)
            {
                foreach (JArray jodarray in jarray[0])
                {
                    BettingOrder order = new BettingOrder();
                    order = ResolveJsonToOrder(order, jodarray);
                    result.Add(order);
                }
                foreach (JArray jodarray in jarray[1])
                {
                    BettingOrder order = new BettingOrder();
                    order = ResolveJsonToOrder(order, jodarray);
                    result.Add(order);
                }
            }
            return result;
        }

        public async override Task<BettingResult> RequestBetting(BetTick tick, decimal money)
        {
            if (!ValidSignup()) return null;
            string url = string.Format(@"http://{0}/web-root/restricted/ticket/confirm.aspx", Host);
            string referer = string.Format("http://{0}/web-root/restricted/default.aspx?loginname={1}", Host, SessionLoginName);
            string rqContent = string.Format("loginname={1}&stake={0}&betcount={2}&ostyle=&betpage=&stakeInAuto={0}&betpage=od&acceptIfAny=0&sameticket=0&oid={3}"
                , (int)money, SessionLoginName, tick.BetCount, tick.OddsID);
            var rqUrl = new Uri(url + "?" + rqContent);
            var rqTask = GetHtmlRequest(rqUrl, referer);
            var html = await rqTask;
            if (rqTask.IsCanceled || rqTask.Exception != null)
                return null;
            _loginInfo.Reset(html);
            var result = ResolveBettingResult(html);
            if (result.State == BettingState.Succeed || result.State == BettingState.ToOrder)
                result = await RequestBettingMiniOrder(result, tick);
            return result;
        }

        protected async virtual Task<BettingResult> RequestBettingMiniOrder(BettingResult lastResult, BetTick lastTick)
        {
            if (!ValidSignup()) return null;
            Uri url = FormatUri(@"http://{0}//web-root/restricted/betlist/bet-list-mini-data.aspx", Host);
            string referer = string.Format("http://{0}/web-root/restricted/default.aspx?loginname={1}", Host, SessionLoginName);
            var reqTask = GetHtmlRequest(url, referer);
            var html = await reqTask;
            if (reqTask.IsCanceled || reqTask.Exception != null)
                return null;
            _loginInfo.Reset(html);
            if (string.IsNullOrEmpty(html))
                return lastResult;
            lastResult = ResolveBettingOrder(html, lastResult, lastTick);
            return lastResult;
        }

        protected virtual BettingResult ResolveBettingOrder(string html, BettingResult bettingResult, BetTick lastTick)
        {
            var match = regBettingMiniOrder.Match(html);
            if (match.Success)
            {
                var jsonString = match.Groups["j"].Success ? match.Groups["j"].Value : "";
                jsonString = regJsUnicode.Replace(jsonString, mat => ((char)Int32.Parse(mat.Value.Substring(2), System.Globalization.NumberStyles.HexNumber))
                    .ToString(System.Globalization.CultureInfo.InvariantCulture));
                jsonString = regJsHex.Replace(jsonString, mat => ((char)Int32.Parse(mat.Value.Substring(2), System.Globalization.NumberStyles.HexNumber))
                        .ToString(System.Globalization.CultureInfo.InvariantCulture));
                var jarray = (JArray)JsonConvert.DeserializeObject(jsonString);
                var orders = ResolveJsonToOrders(jarray);
                if (bettingResult.State == BettingState.Succeed && !string.IsNullOrEmpty(bettingResult.Order.RecID))
                {
                    var order = orders.FirstOrDefault(o => o.RecID == bettingResult.Order.RecID);
                    if (order != null)
                        bettingResult.Order = order;
                }
                else
                {
                    var maxTime = orders.Max(o => o.BettingTime.HasValue ? o.BettingTime.Value : DateTime.MinValue);
                    var query = orders.Where(o => o.BettingTime.HasValue && o.BettingTime.Value == maxTime);
                    foreach (var item in query)
                    {
                        if (item.RawHomeTeam == lastTick.HomeTeam && item.RawAwayTeam == lastTick.AwayTeam)
                        {
                            bettingResult.Order = item;
                            break;
                        }
                    }
                }
            }
            return bettingResult;
        }

        protected virtual IList<BettingOrder> ResolveJsonToOrders(JArray jarray)
        {
            List<BettingOrder> result = new List<BettingOrder>();
            if (jarray.Count == 2)
            {
                foreach (JArray jitemArray in jarray[0])
                {
                    BettingOrder order = new BettingOrder();
                    order = ResolveJsonToOrder(order, jitemArray);
                    result.Add(order);
                }
                foreach (JArray jitemArray in jarray[1])
                {
                    BettingOrder order = new BettingOrder();
                    order = ResolveJsonToOrder(order, jitemArray);
                    result.Add(order);
                }
            }
            return result;
        }

        protected virtual BettingOrder ResolveJsonToOrder(BettingOrder currOrder, JArray jsonArray)
        {
            currOrder.BmID = BookerID;
            currOrder.Account = Account;
            currOrder.RecID = jsonArray[1].Value<string>();
            currOrder.RawTypeName = jsonArray[4].Value<string>();
            currOrder.RawLeagueName = jsonArray[8].Value<string>();
            currOrder.RawHomeTeam = jsonArray[9].Value<string>();
            currOrder.RawAwayTeam = jsonArray[10].Value<string>();
            currOrder.RawStatus = jsonArray[6].Value<string>();
            currOrder.OddsValue = jsonArray[12].TryCaseValue<decimal>(0m);
            currOrder.Money = jsonArray[5].TryCaseValue<decimal>(0m);
            var time = jsonArray[2].Value<string>();
            if (!string.IsNullOrEmpty(time))
            {
                var pos = time.IndexOf(' ');
                if (pos > -1)
                    time = time.Insert(pos, string.Format("/{0}", DateTime.Now.Year));
                DateTime betime;
                if (DateTime.TryParse(time, out betime))
                {
                    currOrder.BettingTime = betime;
                }
            }
            var betTo = jsonArray[11].Value<string>();
            var match = regMiniOrderHandicap.Match(betTo);
            switch (currOrder.RawTypeName)
            {
                case "1X2":
                    currOrder.Type = MarketType.M1X2;
                    if (betTo == "1")
                        currOrder.PriceType = OddsPrice.OddsPriceType.Home;
                    else if (betTo == "2")
                        currOrder.PriceType = OddsPrice.OddsPriceType.Away;
                    else if (betTo == "X")
                        currOrder.PriceType = OddsPrice.OddsPriceType.Tie;
                    break;
                case "亚洲盘":
                    currOrder.Type = MarketType.MHDP;
                    if (match.Success)
                    {
                        var tname = match.Groups["t"].Success ? match.Groups["t"].Value : string.Empty;
                        if (tname == currOrder.RawHomeTeam)
                            currOrder.PriceType = OddsPrice.OddsPriceType.Home;
                        else if (tname == currOrder.RawAwayTeam)
                            currOrder.PriceType = OddsPrice.OddsPriceType.Away;
                        decimal handicap = 0m;
                        decimal.TryParse(match.Groups["h"].Success ? match.Groups["h"].Value : "0", out handicap);
                        currOrder.Handicap = handicap;
                    }
                    break;
                case "大小盘":
                    currOrder.Type = MarketType.MOU;
                    if (match.Success)
                    {
                        decimal handicap = 0m;
                        decimal.TryParse(match.Groups["h"].Success ? match.Groups["h"].Value : "0", out handicap);
                        currOrder.Handicap = handicap;
                        var tname = match.Groups["t"].Success ? match.Groups["t"].Value : string.Empty;
                        if (tname == "大")
                            currOrder.PriceType = OddsPrice.OddsPriceType.Over;
                        else if (tname == "小")
                            currOrder.PriceType = OddsPrice.OddsPriceType.Under;

                    }
                    break;
                default:
                    break;
            }
            return currOrder;
        }

        protected virtual BettingResult ResolveBettingResult(string html)
        {
            BettingResult result = new BettingResult { Message = "未知", State = BettingState.Unknow };

            if (html.IndexOf("您的总投注已超过单场比赛的最高投注") > -1)
            {
                result.State = BettingState.Failed;
                result.Message = "您的总投注已超过单场比赛的最高投注";
                return result;
            }
            int pos = html.IndexOf("onUpdateBetReceipt");
            if (pos > -1)
            {
                var match = regBettingResultOrder.Match(html);
                if (match.Success)
                {
                    var recId = match.Groups["id"].Success ? match.Groups["id"].Value : "";
                    var jsonStr = match.Groups["j"].Success ? match.Groups["j"].Value : "";
                    JArray jarray = (JArray)JsonConvert.DeserializeObject(jsonStr);
                    result.State = BettingState.Succeed;
                    result.Message = jarray[7].Value<string>();
                    result.Order = new BettingOrder()
                    {
                        RecID = recId,
                    };
                    return result;
                }
            }
            else
            {
                pos = html.IndexOf("alert");
                if (pos > -1)
                {
                    int posBegin = html.IndexOf("'", pos);
                    int posEnd = html.IndexOf("'", pos);
                    var mess = html.Substring(posBegin + 1, posEnd - posBegin);
                    result.State = BettingState.Reset;
                    result.Message = mess;
                }
                else
                {
                    var match = regBettingResultCode.Match(html);
                    if (match.Success)
                    {
                        var code = match.Groups["c"].Success ? match.Groups["c"].Value : "";
                        if (code == "101" || code == "102")
                        {
                            result.State = BettingState.ToOrder;
                            result.Order = new BettingOrder();
                        }
                        else
                        {
                            result.State = BettingState.Failed;
                            result.Message = "打货失败，请刷新重试";
                        }
                    }
                }
            }
            return result;
        }
        #endregion

        #region request bet ticks
        public override async Task<BetTick> RequestTick(BetTick oldTick)
        {
            if (!ValidSignup()) return null;

            var op = PriceTypeMappingAlias[oldTick.PriceType];
            if (oldTick.MarketType == Odds.MarketType.FH1X2 || oldTick.MarketType == Odds.MarketType.G1X2)
                op = op[2].ToString();
            else if (oldTick.MarketType == Odds.MarketType.FHHDP || oldTick.MarketType == Odds.MarketType.GHDP)
                op = op[0].ToString();
            string dataUrl = string.Format(@"http://{0}/web-root/restricted/ticket/ticket.aspx?loginname={1}&id={2}&odds={3:N2}&op={4}&isor=-1&isLive=1&style=1&hdpType=&hidRefreshOdds=refresh&hidAuto=&betpage=0"
                            , Host, SessionLoginName, oldTick.OddsID, oldTick.Price, op);
            string referer = string.Format(@"http://{0}/web-root/restricted/default.aspx?loginname={1}&redirect=true", Host, SessionLoginName);
            var reqTask = GetHtmlRequest(new Uri(dataUrl), referer);
            var result = await reqTask;
            if (string.IsNullOrEmpty(result) || reqTask.Exception != null || reqTask.IsCanceled)
                return null;
            _loginInfo.Reset(result);

            int dataPos = result.IndexOf("$M('ticket').onUpdate");
            if (dataPos < 0) return oldTick;
            int beginPos = result.IndexOf("[", dataPos);
            int endPos = result.LastIndexOf(']');
            string jsonString = result.Substring(beginPos, endPos - beginPos + 1);
            var json = (JArray)JsonConvert.DeserializeObject(jsonString);
            if (json[12].Type != JTokenType.Null)
                oldTick.MinAmount = json[12].Value<decimal>();
            if (json[13].Type != JTokenType.Null)
                oldTick.MaxAmount = json[13].Value<decimal>();
            if (json[23].Type == JTokenType.Integer)
                oldTick.HomeScore = json[23].Value<byte>();
            if (json[24].Type == JTokenType.Integer)
                oldTick.AwayScore = json[24].Value<byte>();
            if (json[9].Type != JTokenType.Null)
                oldTick.Price = json[9].Value<decimal>();
            else if (json[10].Type != JTokenType.Null)
                oldTick.Price = json[10].Value<decimal>();
            if (json[19].Type != JTokenType.Null)
                oldTick.BetCount = json[19].Value<int>();
            oldTick.UpdateAccount = Account;
            oldTick.UpdateHost = Host;
            return oldTick;
        }

        public override async Task<BetTick> RequestTick(string oddsId, OddsPrice price)
        {
            if (!ValidSignup()) return null;

            var op = PriceTypeMappingAlias[price.Type];
            if (price.MkType == Odds.MarketType.FH1X2 || price.MkType == Odds.MarketType.G1X2)
                op = op[2].ToString();
            else if (price.MkType == Odds.MarketType.FHHDP || price.MkType == Odds.MarketType.GHDP)
                op = op[0].ToString();
            string dataUrl = string.Format(@"http://{0}/web-root/restricted/ticket/ticket.aspx?loginname={1}&id={2}&op={4}&odds={3:N2}&hdpType=&isor=0&isLive=0&betpage=5&style=1"
                                        , Host, SessionLoginName, oddsId, price.Price, op);
            string referer = string.Format(@"http://{0}/web-root/restricted/default.aspx?loginname={1}&redirect=true", Host, SessionLoginName);
            var reqTask = GetHtmlRequest(new Uri(dataUrl), referer);
            var result = await reqTask;
            if (string.IsNullOrEmpty(result) || reqTask.Exception != null || reqTask.IsCanceled)
                return null;
            _loginInfo.Reset(result);

            int dataPos = result.IndexOf("$M('ticket').onUpdate");
            if (dataPos < 0) return null;
            int beginPos = result.IndexOf("[", dataPos);
            int endPos = result.LastIndexOf(']');

            string jsonString = result.Substring(beginPos, endPos - beginPos + 1);
            jsonString = regJsUnicode.Replace(jsonString, mat => ((char)Int32.Parse(mat.Value.Substring(2), System.Globalization.NumberStyles.HexNumber))
                                .ToString(System.Globalization.CultureInfo.InvariantCulture));
            jsonString = regJsHex.Replace(jsonString, mat => ((char)Int32.Parse(mat.Value.Substring(2), System.Globalization.NumberStyles.HexNumber))
                    .ToString(System.Globalization.CultureInfo.InvariantCulture));
            var json = (JArray)JsonConvert.DeserializeObject(jsonString);
            BetTick tick = new BetTick();
            tick.BmID = BookerID;
            tick.OddsID = oddsId;
            tick.GameID = json[28].Value<string>();
            tick.HomeTeam = json[2].Value<string>();
            tick.AwayTeam = json[3].Value<string>();
            tick.HomeScore = json[23].Value<byte>();
            tick.AwayScore = json[24].Value<byte>();
            tick.MinAmount = json[12].Value<decimal>();
            tick.MaxAmount = json[13].Value<decimal>();
            if (json[9].Type != JTokenType.Null)
                tick.Price = json[9].Value<decimal>();
            else
                tick.Price = json[10].Value<decimal>();
            tick.BetCount = json[19].Value<int>();
            tick.UpdateAccount = Account;
            tick.UpdateHost = Host;

            if (json[6].Type != JTokenType.Null)
            {
                string handicpStr = json[6][0].Value<string>();
                var handicpStrArray = handicpStr.Split(':');
                if (handicpStrArray.Length > 1)
                {
                    decimal handicap = 0m;
                    if (decimal.TryParse(handicpStrArray[1], out handicap)) tick.Handicap = handicap;
                }
            }
            tick.PriceType = price.Type;
            tick.MarketType = price.MkType;
            return tick;
        }
        #endregion

        #region request live odds data
        public virtual async Task<IList<Game>> RequestLiveOdds(bool resetVersion)
        {
            if (!ValidSignup()) return null;

            if (resetVersion) LiveSync.Version = 0;
            OddsDisplayParamters.EventData = 2;
            string dataUrl = string.Format("http://{0}/web-root/restricted/odds-display/today-data.aspx?od-param={1}&fi=0&v={2}"
                                    , Host, OddsDisplayParamters.ToString(), LiveSync.Version);
            string referer = string.Format("http://{0}/web-root/restricted/default.aspx?loginname={1}&redirect=true"
                                                , Host, SessionLoginName);

            var reqTask = GetHtmlRequest(new Uri(dataUrl), referer);
            string result = await reqTask;
            LiveSync.LastRequestTime = DateTime.Now;
            LiveSync.RequestTime++;

            if (reqTask.Exception != null || reqTask.IsCanceled)
                return null;
            if (string.IsNullOrEmpty(result))
                return null;
            _loginInfo.Reset(result);

            var matLiveData = regLiveData.Match(result);
            var matIdlData = regLiveIdleData.Match(result);
            if (!matLiveData.Success)
            {
                if (matIdlData.Success)
                {
                    var jidl = (JArray)JsonConvert.DeserializeObject("[" + matIdlData.Groups["j"].Value + "]");
                    LiveSync.Version = jidl[0].TryCaseValue(LiveSync.Version);
                }
                return null;
            }
            string json = string.Format("[{0}]", matLiveData.Groups["j"]);
            OddsDisplayParamters.Token = 2;

            var games = ResolveDataJson(json, LiveSync, true, !resetVersion, true);
            return games;


        }
        public override async Task<bool> RequestUpdateLiveOdds()
        {
            var result = await RequestLiveOdds(false);
            return true;
        }

        public override async Task<IList<Game>> RequestLiveFullOdds()
        {
            return await RequestLiveOdds(true);
        }


        public virtual async Task<IList<Game>> RequestTodayOdds(bool resetVersion)
        {
            if (!ValidSignup()) return null;
            if (resetVersion) NoLiveSync.Version = 0;
            OddsDisplayParamters.EventData = 2;
            string dataUrl = string.Format("http://{0}/web-root/restricted/odds-display/today-data.aspx?od-param={1}&fi=1&v={2}&dl={3}"
                                                , Host, OddsDisplayParamters.ToString(), NoLiveSync.Version, OddsDisplayParamters.DaylightType);
            string referer = string.Format("http://{0}/web-root/restricted/default.aspx?loginname={1}&redirect=true"
                                                , Host, SessionLoginName);

            var reqTask = GetHtmlRequest(new Uri(dataUrl), referer);
            string result = await reqTask;
            NoLiveSync.LastRequestTime = DateTime.Now;
            NoLiveSync.RequestTime += 1;

            if (reqTask.Exception != null || reqTask.IsCanceled)
                return null;
            if (string.IsNullOrEmpty(result))
                return null;
            _loginInfo.Reset(result);

            var matLiveData = regLiveData.Match(result);
            var matIdlData = regLiveIdleData.Match(result);
            if (!matLiveData.Success)
            {
                if (matIdlData.Success)
                {
                    var jidl = (JArray)JsonConvert.DeserializeObject("[" + matIdlData.Groups["j"].Value + "]");
                    NoLiveSync.Version = jidl[0].TryCaseValue(NoLiveSync.Version);
                }
                return null;
            }

            string json = string.Format("[{0}]", matLiveData.Groups["j"]);
            OddsDisplayParamters.Token = 2;

            var games = ResolveDataJson(json, NoLiveSync, false, !resetVersion, true);
            return games;
        }

        public override async Task<bool> RequestUpdateTodayOdds()
        {
            var result = await RequestTodayOdds(false);
            return true;
        }

        public override async Task<IList<Game>> RequestTodayFullOdds()
        {
            return await RequestTodayOdds(true);
        }

        public override async Task<IList<Game>> RequestEarlyMarketOdds()
        {
            if (!ValidSignup()) return null;

            EarlyMarketSync.Version = 0;
            EarlyDisplayParamters.Token = 1;
            EarlyDisplayParamters.EventData = 8;
            EarlyDisplayParamters.MarketPage = 2;
            string dataUrl = string.Format("http://{0}/web-root/restricted/odds-display/early-market-data.aspx?od-param={1}&v={2}"
                                                , Host, EarlyDisplayParamters.ToString(), EarlyMarketSync.Version);
            string referer = string.Format("http://{0}/web-root/restricted/default.aspx?loginname={1}&redirect=true"
                                                , Host, SessionLoginName);
            var reqTask = GetHtmlRequest(new Uri(dataUrl), SessionLoginName.Value);
            string result = await reqTask;
            if (reqTask.Exception != null || reqTask.IsCanceled)
                return null;
            if (string.IsNullOrEmpty(result))
                return null;
            _loginInfo.Reset(result);

            var matLiveData = regLiveData.Match(result);
            if (!matLiveData.Success)
                return null;
            EarlyMarketSync.LastRequestTime = DateTime.Now;
            EarlyMarketSync.RequestTime += 1;
            string jsonStr = string.Format("[{0}]", matLiveData.Groups["j"]);
            OddsDisplayParamters.Token = 2;
            var games = ResolveDataJson(jsonStr, EarlyMarketSync, false, false, false);
            return games;
        }
        #endregion

        #region resolve data json
        protected virtual IList<Game> ResolveDataJson(string jsonString, SboSyncData syncData, bool isLive, bool isUpdate, bool hasClear)
        {
            var json = (JArray)JsonConvert.DeserializeObject(jsonString);
            try
            {
                int version = json[0].Value<int>();
                int frame = json[1].Value<int>();
                //int clear = json[2].Value<int>();
                var jbmevents = hasClear ? json[3] as JArray : json[2] as JArray;

                if (syncData.Version < version)
                    syncData.Version = version;
                if (jbmevents == null) return null;
                var jtournaments = jbmevents[0] as JArray;
                var jevents = jbmevents[1] as JArray;
                var jeventResult = jbmevents[2] as JArray;
                var jeventResultExt = jbmevents[3] as JArray;
                var joddsArray = jbmevents[5] as JArray;

                var jeventsDelete = jbmevents[4] as JArray;
                var joddsDelete = jbmevents[6] as JArray;

                if ((jeventResult == null || jeventResult.Count == 0) && !isUpdate)
                    return null;

                var games = new List<Game>();
                var oddsRange = new List<Odds>();
                var marketRange = new List<Market>();
                Dictionary<string, SboEventResult> sboResultDic = new Dictionary<string, SboEventResult>();
                Dictionary<int, string> tournaments = new Dictionary<int, string>();
                if (jeventResult != null)
                {
                    ResolveEventResultJson(sboResultDic, jeventResult);
                    if (isUpdate && RepositoryService != null)
                    {
                        var sboIds = sboResultDic.Select(gr => gr.Value.ResultID).ToArray();
                        var updateGames = RepositoryService.GetGames(BookerID, sboIds);
                        var updateMarket = RepositoryService.GetMarket(BookerID, Host, sboIds);
                        foreach (var gm in updateGames)
                        {
                            games.Add(gm);
                            gm.LastUpdateTime = DateTime.Now;
                            gm.State = isLive ? Game.GameState.InProgress : Game.GameState.PreGame;
                        }
                        marketRange.AddRange(updateMarket);
                    }
                }
                if (jtournaments != null)
                    ResolveTournamentsJson(tournaments, jtournaments);
                if (jevents != null)
                    ResolveGamesJson(games, marketRange, tournaments, sboResultDic, isLive, isUpdate, jevents);

                if (jeventResultExt != null)
                    ResolveEventResultExtJson(games, jeventResultExt);
                if (joddsArray != null)
                {
                    if (isUpdate)
                        ResolveUpdateMarketJson(games, oddsRange, marketRange, isLive, joddsArray);
                    else
                        ResolveNewMarketJson(games, oddsRange, marketRange, isLive, joddsArray);
                }

                if ((joddsDelete is JArray) && RepositoryService != null)
                    RepositoryService.DisableOdds(BookerID, Host, joddsDelete.Select(jo => jo.Value<string>()).ToArray());
                //只删除直播数据
                if ((jeventsDelete is JArray) && RepositoryService != null && isLive)
                    RepositoryService.DisableGames(BookerID, jeventsDelete.Select(je => je.Value<string>()).ToArray());
                if (isUpdate)
                {
                    if (games.Count > 0)
                        RepositoryService.UpdateAllGame(games);
                    if (marketRange.Count > 0)
                        RepositoryService.UpdateAllMarket(marketRange);
                    if (oddsRange.Count > 0)
                        RepositoryService.UpdateAllOdds(oddsRange);
                }
                else
                {
                    if (RepositoryService != null) RepositoryService.UpdateRelatedGame(games, marketRange, oddsRange);
                }
                return games;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        protected virtual void ResolveGamesJson(IList<Game> games
            , IList<Market> markets
            , Dictionary<int, string> tournaments
            , Dictionary<string, SboEventResult> evtResultDic
            , bool isLive, bool isUpdate, JArray jevents)
        {
            foreach (var jevt in jevents)
            {
                var evtId = jevt[0].Value<string>();
                var result = evtResultDic.FirstOrDefault(r => r.Value.EventID == evtId);
                var gid = result.Value.ResultID;
                Game gm = null;
                Market mk = null;
                if (isUpdate)
                {
                    gm = games.FirstOrDefault(g => g.GameID == gid);
                    mk = markets.FirstOrDefault(m => m.GameID == gid);
                }
                if (gm == null)
                {
                    gm = new Game();
                    gm.BmID = BookerID;
                    gm.GameID = gid;
                    gm.GameCode = evtId;
                    games.Add(gm);
                }
                if (mk == null)
                {
                    mk = new Market();
                    mk.Host = Host;
                    mk.GameID = gid;
                    mk.BmID = BookerID;
                    markets.Add(mk);
                }
                mk.UpdateAccount = Account;
                mk.UpdateTime = DateTime.Now;

                gm.LastUpdateTime = DateTime.Now;
                gm.LastUpdateHost = Host;
                gm.HomeScore = result.Value.HomeScore.HasValue ? result.Value.HomeScore.Value : gm.HomeScore;
                gm.AwayScore = result.Value.AwayScore.HasValue ? result.Value.AwayScore.Value : gm.AwayScore;
                if (jevt[3].Type != JTokenType.Null)
                    gm.HomeTeam = jevt[3].Value<string>();
                if (jevt[4].Type != JTokenType.Null)
                    gm.AwayTeam = jevt[4].Value<string>();
                if (jevt[7].Type != JTokenType.Null)
                    gm.StartTime = jevt[7].Value<DateTime>();

                if (jevt[2].Type == JTokenType.Integer)
                {
                    var tourId = jevt[2].Value<int>();
                    if (tournaments.ContainsKey(tourId))
                    {
                        gm.Tournament = tournaments[tourId];
                        gm.TournamentID = tourId.ToString();
                    }
                }

                if (isLive)
                {
                    gm.State = Game.GameState.InProgress;
                    if (RepositoryService != null) RepositoryService.UpdatePreGameOdds(BookerID, gm.GameID);
                }
                else
                    gm.State = Game.GameState.PreGame;
                gm.AppendRawJson(jevt.ToString());
                gm.AppendRawJson(result.Value.RawJsonString);

            }
        }

        protected virtual void ResolveTournamentsJson(Dictionary<int, string> tournaments, JArray jtournaments)
        {
            foreach (var jtour in jtournaments)
            {
                if ((jtour is JArray) && ((JArray)jtour).Count > 2)
                {
                    if (jtour[0].Type == JTokenType.Integer && jtour[1].Type != JTokenType.Null)
                    {
                        var tourId = jtour.Value<int>(0);
                        var tourName = jtour.Value<string>(1);
                        if (tournaments.ContainsKey(tourId))
                            continue;
                        tournaments.Add(tourId, tourName);
                    }
                }
            }
        }

        protected virtual void ResolveEventResultJson(Dictionary<string, SboEventResult> evtResultDic, JArray jeventResult)
        {
            foreach (var jret in jeventResult)
            {
                if (jret is JArray && ((JArray)jret).Count > 4)
                {
                    if (jret[0].Type == JTokenType.Integer && jret[1].Type == JTokenType.Integer)
                    {
                        SboEventResult result = new SboEventResult();
                        var resultId = jret[0].Value<string>();
                        var evtId = jret[1].Value<string>();
                        result.ResultID = resultId;
                        result.EventID = evtId;
                        if (jret[3].Type == JTokenType.Integer)
                            result.HomeScore = jret[3].Value<byte>();
                        if (jret[4].Type == JTokenType.Integer)
                            result.AwayScore = jret[4].Value<byte>();
                        result.RawJsonString = jret.ToString();
                        evtResultDic.Add(resultId, result);
                    }
                }
            }

        }

        protected virtual void ResolveEventResultExtJson(IList<Game> games, JArray jresultExt)
        {
            foreach (var jret in jresultExt)
            {
                if ((jret is JArray) && ((JArray)jret).Count > 7)
                {
                    try
                    {
                        var resultId = jret[0].Value<string>();
                        var game = games.FirstOrDefault(g => g.GameID == resultId);
                        if (game != null)
                        {
                            var pried = jret[2].Value<byte>();
                            var intMin = jret[3].Value<int>();

                            var minutes = (intMin > 255) ? (byte)0 : (byte)intMin;
                            var totalMin = jret[4].Value<int>();
                            var hred = jret[5].Value<byte>();
                            var vred = jret[6].Value<byte>();
                            var injuryTime = jret[7].Value<byte>();
                            game.HomeRed = hred;
                            game.AwayRed = vred;
                            game.PlayMinutes = minutes;
                            game.InjuryMinutes = injuryTime;
                            game.Pried = pried;
                            if (pried == 5)
                                game.PlayeTime = "半场";
                            else
                                game.PlayeTime = string.Format("{0}H {1}'{2}", pried, minutes, injuryTime > 0 ? " +" + injuryTime.ToString() : "");
                            game.AppendRawJson(jret.ToString());
                        }
                    }
                    catch (Exception ex)
                    {
                        throw ex;
                    }
                }
            }
        }

        protected virtual void ResolveNewMarketJson(IList<Game> games
            , IList<Odds> oddsRange
            , IList<Market> marketRange
            , bool isLive, JToken joddsArray)
        {
            foreach (var jodds in joddsArray)
            {
                var oddsId = jodds.Value<string>(0);
                var oddsInfo = (JToken)jodds[1];
                var oddsPrices = (JArray)jodds[2];
                var resultId = oddsInfo.Value<string>(0);

                var game = games.FirstOrDefault(g => g.GameID == resultId);
                var market = marketRange.FirstOrDefault(m => m.GameID == resultId);
                if (game != null)
                {
                    var marketId = oddsInfo.Value<int>(1);
                    var marketType = (SboMarketType)marketId;
                    if (MarketTypeMapping.ContainsKey(marketType))
                    {
                        var odds = new Odds(oddsId, MarketTypeMapping[marketType]);
                        odds.BmID = BookerID;
                        odds.GameID = game.GameID;
                        odds.IsLive = isLive;
                        odds.IsAvailable = true;
                        odds.LastUpdateTime = DateTime.Now;
                        odds.LastUpdateAccount = Account;
                        odds.Handicap = oddsInfo.Value<decimal>(4);
                        if (!market.OddsIDS.Contains(odds.OddsID))
                            market.OddsIDS.Add(odds.OddsID);

                        oddsRange.Add(odds);

                        ResolveOddsPriceJson(odds, marketType, false, oddsPrices);
                    }
                }
            }
        }

        protected virtual void ResolveUpdateMarketJson(IList<Game> games, IList<Odds> oddsRange, IList<Market> marketRange, bool isLive, JToken joddsArray)
        {
            if (RepositoryService == null) return;
            foreach (var jodds in joddsArray)
            {
                try
                {
                    var oddsId = jodds[0].Value<string>();
                    var oddsInfo = jodds[1] as JArray;
                    var resultId = oddsInfo == null ? string.Empty : oddsInfo.Value<string>(0);
                    var oddsPrices = ((JArray)jodds).Count > 2 ? jodds[2] as JArray : null;

                    var game = games.FirstOrDefault(g => g.GameID == resultId);
                    var market = marketRange.FirstOrDefault(m => m.GameID == resultId);

                    Odds odds = RepositoryService.GetOdds(BookerID, oddsId);
                    if (odds != null)
                    {
                        if (market == null)
                        {
                            market = RepositoryService.GetMarket(BookerID, odds.GameID, Host);
                            if (market != null)
                                marketRange.Add(market);
                        }
                        if (market != null)
                        {
                            market.UpdateAccount = Account;
                            market.UpdateTime = DateTime.Now;
                            if (!market.OddsIDS.Contains(odds.OddsID))
                                market.OddsIDS.Add(odds.OddsID);
                        }
                        odds.IsLive = isLive;
                        odds.IsAvailable = true;
                        odds.LastUpdateTime = DateTime.Now;
                        odds.LastUpdateAccount = Account;
                        if (oddsInfo != null && oddsInfo.Count > 4 && oddsInfo[4].Type != JTokenType.Null)
                            odds.Handicap = oddsInfo[4].TryCaseValue<decimal>(odds.Handicap);
                        oddsRange.Add(odds);
                        var mType = MarketTypeMapping.FirstOrDefault(mtm => mtm.Value == odds.Type);
                        if (oddsPrices != null)
                            ResolveOddsPriceJson(odds, mType.Key, true, oddsPrices);
                    }
                    else if (game != null && market != null)
                    {
                        var marketId = oddsInfo.Value<int>(1);
                        var marketType = (SboMarketType)marketId;
                        if (MarketTypeMapping.ContainsKey(marketType))
                        {
                            odds = new Odds(oddsId, MarketTypeMapping[marketType]);
                            odds.BmID = BookerID;
                            odds.GameID = game.GameID;
                            odds.IsLive = isLive;
                            odds.IsAvailable = true;
                            odds.LastUpdateTime = DateTime.Now;
                            odds.LastUpdateAccount = Account;
                            if (oddsInfo != null && oddsInfo.Count > 4 && oddsInfo[4].Type != JTokenType.Null)
                                odds.Handicap = oddsInfo[4].TryCaseValue<decimal>(odds.Handicap);
                            if (!market.OddsIDS.Contains(odds.OddsID))
                                market.OddsIDS.Add(odds.OddsID);

                            oddsRange.Add(odds);
                            ResolveOddsPriceJson(odds, marketType, false, oddsPrices);
                        }
                    }
                }
                catch (Exception ex)
                {
                    throw ex;
                }
            }
        }

        protected virtual void ResolveOddsPriceJson(Odds odds, SboMarketType type, bool isUpdate, JArray jPriceArray)
        {
            if (MarketPriceMapping.ContainsKey(type))
            {
                var priceIndexType = MarketPriceMapping[type];
                for (int i = 0; i < priceIndexType.Length; i++)
                {
                    if (i < jPriceArray.Count)
                    {
                        if (jPriceArray[i].Type != JTokenType.Null)
                        {
                            var priceValue = jPriceArray[i].Value<decimal>();
                            if (isUpdate)
                            {
                                var price = odds.Prices.FirstOrDefault(p => p.Type == priceIndexType[i]);
                                if (price != null)
                                    price.Price = priceValue;
                                else
                                    odds.Prices.Add(new OddsPrice(priceValue, priceIndexType[i]));
                            }
                            else
                            {
                                var price = new OddsPrice(priceValue, priceIndexType[i]);
                                odds.Prices.Add(price);
                            }
                        }
                    }
                }
            }
        }
        #endregion

    }
}
